﻿using DependencyInjectionHelperLib.Eunms;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace DependencyInjectionHelperLib.Helpers
{
    internal static class DIRegistrationHelper
    {
        /// <summary>
        /// 获取当前程序集及其引用的程序集中，特性类型为 TAttribute 的列表。
        /// </summary>
        /// <typeparam name="TAttribute">特性类型</typeparam>
        /// <returns>特性类型为 TAttribute 的列表</returns>
        internal static IEnumerable<Type> GetTAttributeTypes<TAttribute>()
            where TAttribute : Attribute
        {
            // 获取当前程序集及其引用的程序集
            var assemblies = Assembly
                .GetEntryAssembly()!
                .GetReferencedAssemblies()
                .Select(Assembly.Load)
                .Concat(new List<Assembly> { Assembly.GetEntryAssembly()! });
            // 提前加载所有程序集到列表中

            // 使用 Parallel.SelectMany 来并行获取所有类型
            var types = assemblies
                .AsParallel()
                .SelectMany(x => x.GetTypes())
                // 过滤出具有指定自定义属性的非抽象类
                .Where(t => t != null && t.IsClass && !t.IsAbstract && t.CustomAttributes.Any(p => p.AttributeType == typeof(TAttribute)))
                .Distinct(); // 去除重复
            return types;
        }

        /// <summary>
        /// 将服务注册到容器中
        /// </summary>
        /// <typeparam name="TAttribute">特性类型</typeparam>
        /// <param name="serviceCollection">容器</param>
        /// <param name="OnAttributeFound">对遍历的特性要执行的操作</param>
        internal static void RegisterServices<TAttribute>(
            IServiceCollection serviceCollection,
            Action<TAttribute, IServiceCollection, Type, Dictionary<Lifetimes, Action<IServiceCollection, Type, Type>>> OnAttributeFound
        )
            where TAttribute : Attribute
        {
            var types = GetTAttributeTypes<TAttribute>();

            // serviceCollection, serviceType,implementationType
            var registerMethods = new Dictionary<Lifetimes, Action<IServiceCollection, Type, Type>>
            {
                { Lifetimes.Singleton, (s, t, i) => s.AddSingleton(t, i) },
                { Lifetimes.Transient, (s, t, i) => s.AddTransient(t, i) },
                { Lifetimes.Scoped, (s, t, i) => s.AddScoped(t, i) }
            };

            foreach (var type in types)
            {
                var tAttribute = type.GetCustomAttribute<TAttribute>();
                OnAttributeFound(tAttribute!, serviceCollection, type, registerMethods);
            }
        }
    }
}